#ifndef UTIL_HPP  
#define UTIL_HPP  


#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include<sys/stat.h>
#include"bundle.h"
#include<experimental/filesystem>
#include<jsoncpp/json/json.h>
namespace fs=std::experimental::filesystem;

namespace cloud{
class FileUtil{
private:
std::string _filename;
public:
FileUtil(const std::string &filename):_filename(filename){}
bool Remove()
{   if(this->Exists()==false){
      return true;

}
  remove(_filename.c_str());
  return true;


}
int64_t FileSize()//返回文件大小
{
struct stat st;
if(stat(_filename.c_str(),&st)<0)   //使用stat函数，对该文件做解析，将文件属性放到st结构体中
{
std::cout<<"get file size failed!\n";  //如果返回<0,说明可能没有该文件
return -1;
}
return st.st_size;     //返回该文件大小

}


time_t LastATime()//最后一次访问时间
{
struct stat st;    //因为文件属性都保存到该结构体里面了
if(stat(_filename.c_str(),&st)<0)
{
std::cout<<"get file size failed!\n";
return -1;
}
return st.st_atim.tv_sec;    //访问文件最后访问时间
}


time_t LastMTime()//最后一次修改时间
{
struct stat st;
if(stat(_filename.c_str(),&st)<0)
{
std::cout<<"get file size failed!\n";
return -1;
}
return st.st_mtim.tv_sec;//访问文件最后修改时间
}

std::string FileName()
{  //_filename="./zjw/lesson59/cloud.cpp"
   size_t pos=_filename.find_last_of("/");//先找最后一个/，如果没有的话，直接是文件名
   if(pos==std::string::npos)
   { return _filename;
     
   }
   
   return _filename.substr(pos+1);//截取文件名
}

bool GetPosLen(std::string*body,size_t pos,size_t len)//body为输出型参数，会将argv[1]文件内容读到body里面去
{

size_t fsize=this->FileSize();                    //读取argv[1]文件大小
if(pos+len>fsize)                                 //如果从要读的地方读len长度，如果大于文件内容大小读取失败
{
  std::cout<<"get file len is error\n";
  return false;
}
std::ifstream ifs;                               //定义一个文件对象
ifs.open(_filename,std::ios::binary);            //以二进制方式打开argv[1]文件
if(ifs.is_open()==false)                         //打开失败的话
{

std::cout<<"open file failed!\n";
return false;


}
ifs.seekg(pos,std::ios::beg);                    //走到这的话，说明可以读
body->resize(len);                               //字符串body扩容到argv[1]文件内容大小
ifs.read(&(*body)[0],len);                       //将文件内容读到body字符串中
if(ifs.good()==false)                            //文件流错误的话，说明读失败
{
   std::cout<<"get file content failed\n";
   ifs.close();
   return false;
}
ifs.close();                                          //读成功，body输出型参数
return true;
}



bool GetContent(std::string*body)                   //复用上面的接口，读取整个文件
{
 size_t fsize=this->FileSize();
 return GetPosLen(body,0,fsize);
}


bool SetContent(const std::string &body)           //将读到内容写到另一个文件
{
   std::ofstream ofs;                             
   ofs.open(_filename,std::ios::binary);              //以二进制方式打开另一个要写入的文件
   if(ofs.is_open()==false)
   {
    std::cout<<"write open file  failed!\n";
    return false;
   }

   ofs.write(&body[0],body.size());                 //将刚才读到的内容，写到打开的另一个文件中
   if(ofs.good()==false)
   {
     std::cout<<"write file content failed!\n";
     ofs.close();
     return false;
   }

    ofs.close();
     return true;


}

bool Compress(const std::string &packname)//压缩，参数为压缩后的文件路径
{
std::string body;
if(this->GetContent(&body)==false)             //获取源文件内容到body中
   {
    std::cout<<"compress get file content failed!\n";
    return false;


   }
std::string packed=bundle::pack(bundle::LZIP,body);   //将源文件获取到body中的内容压缩放到packed中

FileUtil fu(packname);
if(fu.SetContent(packed)==false)                    //将压缩的内容放到新文件中
{
 std::cout<<"compress write packde data failed!\n";
 return false;
}
return true;




}


bool UnCompress(const std::string &filename)//解压  ，参数为解压后保存在的文件
{
  std::string body;
  if(this->GetContent(&body)==false)                 //读取压缩文件内容
  {
    std::cout<<"uncompress get file content failed!\n";
    return false;
 


  }

  std::string unpacked=bundle::unpack(body);         //将读取压缩文件内容解压到unpacked中
  FileUtil fu(filename);
  if(fu.SetContent(unpacked)==false)                 //解压内容写到新文件中去
    {
       std::cout<<"uncompress write packed data failed!\n";
       return false;
    }
    return true;
}

 bool Exists()
 {
   return fs::exists(_filename);   //判断当前文件是否存在
  
 }

 bool CreateDirectory()             //创建目录
 {
   if(this->Exists())return true;   //存在该目录就不创建了
   return fs::create_directories(_filename);
 }


 bool ScanDirectory(std::vector<std::string>*arry) //扫描当前目录下的文件，如果不是目录文件，就将该文件相对路径插入到arry中
 {
  for(auto&p : fs::directory_iterator(_filename))
  {
   if(fs::is_directory(p)==true)
   {
    continue;
   }
   arry->push_back(fs::path(p).relative_path().string());//参数为文件名字符串


  }

  return true;
 }





};


class JsonUtil
{
public:
 static bool Serialize(const Json::Value &root,std::string *str)   //序列化，将root各个字段序列化后放到str中去
 {
     Json::StreamWriterBuilder swb;   //实例化该对象swb用来调用newStreamWriter() 来new一个StreamWriter，
     std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
     std::stringstream ss;                 //字符串流
     sw->write(root,&ss);                  //该函数序列化，写入ss字符流中
     *str=ss.str();                        //输出型参数传出
     return true;

 }


static bool UnSerialize(const std::string&str,Json::Value *root)//反序列化，将str内容反序列化，填到root中各个字段中去去
{
  Json::CharReaderBuilder crb;      //实例化该对象crb用来调用newCharReader() 来new一个CharReader，
  std::unique_ptr<Json::CharReader>cr(crb.newCharReader());//使用智能指针维护
  std::string err;
  bool ret=cr->parse(str.c_str(),str.c_str()+str.size(),root,&err);//将字符串反序列化放到root中
  if(ret==false)
  {std::cout<<"parse error:"<<err<<std::endl;
  return false;}

  return true;

}







};


}
#endif